package com.xjk.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import jodd.http.HttpRequest;;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    private static final String CHARSET_UTF_8 = "UTF-8";

    public static final String CONTENT_TYPE_TEXTHTML = "text/xml; charset=utf-8";

    static RequestConfig defaultRequestConfig = null;

    private static final int defautTimeoutSecond = 20;

    static {
        defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
    }

    /**
     * Garrett请求需要加请求头信息，并且只接受text/plain的content-type
     */
    public static String postGarrett(String url, String body, int outTime, String headerKey, String headerValue) throws Exception {
        logger.info("Garrett url" + url);
        logger.info("Garrett body" + body);
        // 创建SSLClient连接客户端
        CloseableHttpClient client = null;
        if (url.startsWith("https") || url.startsWith("HTTPS")) {
            client = SSLClient.createSSLClient();
        } else {
            client = HttpClientBuilder.create().build();
        }
        // 创建url的post请求对象
        HttpPost post = new HttpPost(url);
        post.addHeader("Content-Type", "application/json,text/plain");
        post.setConfig(makeLocalRequestConfig(outTime));
        ByteArrayEntity entity = new ByteArrayEntity(body.getBytes("UTF-8"));
        // 将请求信息装载到post的entity中
        post.setEntity(entity);
        post.setHeader(headerKey, headerValue);
        post.setConfig(defaultRequestConfig);
        // 请求url获取响应
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String resEntityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            logger.info("url:" + url + "Garrett返回：" + resEntityStr);
            client.close();
            return resEntityStr;
        } else if (response.getStatusLine().getStatusCode() == 404) {
            logger.error("url:" + url + " 404错误~");
            client.close();
            throw new Exception("SSL返回404~");
        } else {
            logger.error("url:" + url + " " + response.getStatusLine().getStatusCode() + "错误~");
            client.close();
            throw new Exception("SSL响应失败~");
        }
    }

    private static RequestConfig makeLocalRequestConfig(int second) {
        RequestConfig requestConfig = null;
        if (second > 0) {
            requestConfig = RequestConfig.copy(defaultRequestConfig).setSocketTimeout(second * 1000).setConnectTimeout(second * 1000)
                .setConnectionRequestTimeout(second * 1000).build();
        } else {
            requestConfig = RequestConfig.copy(defaultRequestConfig).setSocketTimeout(defautTimeoutSecond * 1000)
                .setConnectTimeout(defautTimeoutSecond * 1000).setConnectionRequestTimeout(defautTimeoutSecond * 1000).build();
        }
        return requestConfig;

    }

    /**
     * 向指定URL发送GET方法的请求
     */
    public static String doGet(String url, String param) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = null == param ? url : url + "?" + param;
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            connection.connect();
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }

    /**
     * get请求map参数内容
     */
    public static String getMap(String url, Map<String, String> params) {
        // http客户端创建
        HttpClientBuilder httpClient = HttpClientBuilder.create();
        // 可关闭httpclient获取
        CloseableHttpClient closeableHttpClient = httpClient.build();
        String body = null;
        // 装载post请求数据NameValuePair类型
        HttpGet get = getForm(url, params);
        // post请求http
        body = invoke(closeableHttpClient, get);
        try {
            // 关闭http客户端
            closeableHttpClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return body;
    }

    /**
     * http接口的post请求《用来瑞宏电子发票》
     */
    public static String postMap(String url, Map<String, String> params) {
        // http客户端创建
        HttpClientBuilder httpClient = HttpClientBuilder.create();
        // 可关闭httpclient获取
        CloseableHttpClient closeableHttpClient = httpClient.build();
        String body = null;
        // 装载post请求数据NameValuePair类型
        HttpPost post = postForm(url, params);
        // post请求http
        body = invoke(closeableHttpClient, post);
        try {
            // 关闭http客户端
            closeableHttpClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return body;
    }

    /**
     * http接口的post请求《用来瑞宏电子发票》
     */
    public static String postMap(String url, Map<String, String> params, int socketTimeOut) {
        // http客户端创建
        HttpClientBuilder httpClient = HttpClientBuilder.create();
        // 可关闭httpclient获取
        CloseableHttpClient closeableHttpClient = httpClient.build();
        String body = null;
        // 装载post请求数据NameValuePair类型
        HttpPost post = postForm(url, params);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeOut).build();
        post.setConfig(requestConfig);
        // post请求http
        body = invoke(closeableHttpClient, post);
        try {
            // 关闭http客户端
            closeableHttpClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return body;
    }

    /**
     * 目前用于请求顺丰的https接口
     */
    public static String postSSLMap(String url, Map<String, String> params) throws Exception {
        String result = null;
        CloseableHttpClient client = SSLClient.createSSLClient();
        HttpPost post = postForm(url, params);
        // 请求ssl
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String resEntityStr = EntityUtils.toString(response.getEntity());
            result = resEntityStr;
        } else if (response.getStatusLine().getStatusCode() == 404) {
            throw new Exception("报错~~");
        } else {
            throw new Exception();
        }
        // 关闭http连接
        client.close();
        return result;
    }

    /**
     * 目前用于请求顺丰的https接口
     */
    public static String postSSLMap(String url, Map<String, String> params, int timeout) throws Exception {
        String result = null;
        CloseableHttpClient client = SSLClient.createSSLClient();
        HttpPost post = postForm(url, params);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).build();
        post.setConfig(requestConfig);
        // 请求ssl
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String resEntityStr = EntityUtils.toString(response.getEntity());
            result = resEntityStr;
        } else if (response.getStatusLine().getStatusCode() == 404) {
            throw new Exception("报错~~");
        } else {
            throw new Exception();
        }
        // 关闭http连接
        client.close();
        return result;
    }

    /**
     * 执行http请求
     */
    private static String invoke(HttpClient httpclient, HttpUriRequest httpost) {
        HttpResponse response = sendRequest(httpclient, httpost);
        String body = paseResponse(response);
        return body;
    }

    /**
     * 获取http响应内容
     */
    private static String paseResponse(HttpResponse response) {
        HttpEntity entity = response.getEntity();
        String body = null;
        try {
            body = EntityUtils.toString(entity, "UTF-8");
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return body;
    }

    private static HttpResponse sendRequest(HttpClient httpclient, HttpUriRequest httpost) {
        HttpResponse response = null;
        try {
            response = httpclient.execute(httpost);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }

    /**
     * From post请求
     */
    private static HttpPost postForm(String url, Map<String, String> params) {

        HttpPost httpost = new HttpPost(url);
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        Set<String> keySet = params.keySet();
        for (String key : keySet) {
            nvps.add(new BasicNameValuePair(key, params.get(key)));
        }
        try {
            // log.info("set utf-8 form entity to httppost");
            httpost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return httpost;
    }

    /**
     * From get请求
     */
    private static HttpGet getForm(String url, Map<String, String> params) {
        HttpGet get = new HttpGet(url);
        Iterator<Entry<String, String>> ite = params.entrySet().iterator();
        while (ite.hasNext()) {
            Entry<String, String> entry = ite.next();
            get.setHeader(entry.getKey(), entry.getValue());
        }
        return get;
    }

    /**
     * post请求,现在圆通使用该方法，无法使用上面的post请求方式，原因未知
     */
    public static String post(Object param, String url) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            logger.error("发送 POST 请求出现异常！" + e);
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

    /**
     * post请求,LF使用,设置超时时间30S
     */
    public static String postLF(Object param, String url) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 设置超时时间30S
            conn.setConnectTimeout(30000);// 建立连接的超时时间
            conn.setReadTimeout(30000);// 传递数据的超时时间
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常！" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

    /**
     * http协议接口请求,现用淘宝等
     */
    public static String sendSSL(String url, String body, String contentType) throws Exception {
        // 创建SSLClient连接客户端
        CloseableHttpClient client = SSLClient.createSSLClient();
        // 创建url的post请求对象
        HttpPost post = new HttpPost(url);
        HttpEntity entity = new StringEntity(body, "utf-8");
        // 将请求信息装载到post的entity中
        post.setEntity(entity);
        if (contentType == null || "".equals(contentType)) {
            contentType = "text/html";
        }
        post.setHeader("Content-Type", contentType);
        // 请求url获取响应
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String resEntityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            client.close();
            return resEntityStr;
        } else if (response.getStatusLine().getStatusCode() == 404) {
            client.close();
            throw new Exception("报错~~");
        } else {
            client.close();
            throw new Exception();
        }
    }

    /**
     * http协议TSL接口,現用於nike優惠券激活
     */
    public static String sendTSL(String url, String body, String contentType, int timeout) throws Exception {
        // 创建SSLClient连接客户端
        CloseableHttpClient client = TSLClient.createTSLClient();
        // 创建url的post请求对象
        HttpPost post = new HttpPost(url);
        HttpEntity entity = new StringEntity(body, "utf-8");
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).build();
        // 将请求信息装载到post的entity中
        post.setEntity(entity);
        post.setConfig(requestConfig);
        if (contentType == null || "".equals(contentType)) {
            contentType = "text/html";
        }
        post.setHeader("Content-Type", contentType);
        // 请求url获取响应
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String resEntityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            client.close();
            return resEntityStr;
        } else if (response.getStatusLine().getStatusCode() == 404) {
            client.close();
            throw new Exception("报错~~");
        } else {
            client.close();
            throw new Exception();
        }
    }

    /**
     * http协议接口请求,现用淘宝等
     *
     * @param url
     * @param body
     * @return
     * @throws Exception
     * @Description:
     * @deprecated 请多使用send(String url, Map < String, String > headMap, String body)方法，headMap为null即可
     */
    public static String send(String url, String body) throws Exception {

        CloseableHttpClient client = HttpClientBuilder.create().build();

        HttpPost post = new HttpPost(url);

        HttpEntity entity = new StringEntity(body, "utf-8");
        post.setEntity(entity);
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String resEntityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            client.close();
            return resEntityStr;
        } else if (response.getStatusLine().getStatusCode() == 404) {
            client.close();
            throw new Exception("报错~~");
        } else {
            client.close();
            throw new Exception();
        }
    }

    /**
     * @param url
     * @param headMap,头信息
     * @param body        body内容
     * @return
     * @throws Exception
     * @Description:
     */
    public static String send(String url, Map<String, String> headMap, String body) throws Exception {

        CloseableHttpClient client = HttpClientBuilder.create().build();

        HttpPost post = new HttpPost(url);

        HttpEntity entity = new StringEntity(body, "utf-8");
        post.setEntity(entity);
        if (headMap != null && headMap.size() > 0) {
            Iterator<Entry<String, String>> ite = headMap.entrySet().iterator();
            while (ite.hasNext()) {
                Entry<String, String> entry = ite.next();
                post.setHeader(entry.getKey(), entry.getValue());
            }
        }
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String resEntityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            client.close();
            return resEntityStr;
        } else if (response.getStatusLine().getStatusCode() == 202) {
            client.close();
            return null;
        } else if (response.getStatusLine().getStatusCode() == 404) {
            client.close();
            throw new Exception("报错~~");
        } else {
            client.close();
            throw new Exception();
        }
    }

    /**
     * webservice REST 协议接口请求，现用在香榭格雷
     */
    public static String sendWebservice(String urlStr, String body, String contentType) throws Exception {
        URL url = new URL(urlStr);
        URLConnection conn = url.openConnection();
        conn.setUseCaches(false);
        conn.setDoInput(true);
        conn.setDoOutput(true);

        conn.setRequestProperty("Content-Length", Integer.toString(body.length()));
        conn.setRequestProperty("Content-Type", contentType);

        return getTotalString(body, conn);

    }

    /**
     * webservice REST 协议接口请求，现用在香榭格雷
     */
    public static String sendWebservice(String urlStr, String body) throws Exception {
        URL url = new URL(urlStr);
        URLConnection conn = url.openConnection();
        conn.setUseCaches(false);
        conn.setDoInput(true);
        conn.setDoOutput(true);

        conn.setRequestProperty("Content-Length", Integer.toString(body.length()));
        conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");

        return getTotalString(body, conn);

    }

    private static String getTotalString(String body, URLConnection conn) throws IOException {
        OutputStream os = conn.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET_UTF_8);
        osw.write(body);
        osw.flush();
        osw.close();
        StringBuilder sTotalString = new StringBuilder();
        String sCurrentLine = "";
        InputStream is = conn.getInputStream();
        BufferedReader l_reader = new BufferedReader(new InputStreamReader(is));
        while ((sCurrentLine = l_reader.readLine()) != null) {
            sTotalString.append(sCurrentLine);
        }
        return sTotalString.toString();
    }

    /**
     * put请求
     */
    public static String putMap(String url, Map<String, String> headers, String boby) {
        CloseableHttpClient client = null;
        String body = null;
        try {
            // http客户端创建
            HttpClientBuilder httpClient = HttpClientBuilder.create();
            client = httpClient.build();
            HttpPut put = putForm(url, headers, boby);
            body = invoke(client, put);
        } finally {
            try {
                // 关闭http客户端
                if (null != client) {
                    client.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    private static HttpPut putForm(String url, Map<String, String> headers, String boby) {
        HttpPut httput = new HttpPut(url);
        Set<String> keySet = headers.keySet();
        for (String key : keySet) {
            httput.addHeader(key, headers.get(key));
        }
        StringEntity entity = new StringEntity(boby, "UTF-8");
        httput.setEntity(entity);
        return httput;
    }

    /**
     * post请求
     * @param url
     * @param obj
     * @param <T>
     * @param <V>
     * @return
     */
    public static<T,V> T sendHttpPost(String url, V obj) {
        HttpRequest request = HttpRequest.post(url);
        request.contentType("application/json");
        request.charset(StandardCharsets.UTF_8.name());
        try {
            request.body(JSON.toJSONString(obj));
        } catch (Exception e) {
            System.out.println("发送http请求异常");
        }
        jodd.http.HttpResponse response = request.send();
        response.charset(StandardCharsets.UTF_8.name());
        return JSON.parseObject(response.bodyText(), new TypeReference<T>() {}, Feature.OrderedField);
    }
}
